﻿using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.Xml;
using ProtectThePieWP7.AdditionalClasses;

namespace ProtectThePie
{
    public enum SpriteState
    {
        normal,
        fadingOut
    }

    public class Texel
    {       
        private int start;
        private int end;
        private int animInterval;
        private int counter;        
        private bool Animated;

        public int layerDepth;
        public int Current;

        /// <summary>
        /// Default constructor for texel
        /// </summary>
        /// <param name="Start">Startinf texel</param>
        /// <param name="End">Ending Texel</param>
        /// <param name="AnimationInterval">Frame interval between texels</param>
        /// <param name="layerDepth">Layer of drawing</param>
        public Texel(int Start, int End, int AnimationInterval, int layerDepth)
        {
            this.start = Start;
            this.end = End;
            this.animInterval = AnimationInterval;
            this.Current = Start;
            this.counter = 0;
            this.layerDepth = layerDepth;
            this.Animated = true;
        }

        /// <summary>
        /// Cinstructor for non animated texel
        /// </summary>
        /// <param name="Start">texel to draw</param>
        public Texel(int Start, int layerDepth)
        {
            this.layerDepth = layerDepth;
            Current = Start;
            Animated = false;
        }

        public bool Update()
        {
            if (Animated)
            {
                counter++;
                if (counter == animInterval)
                {
                    if (Current == end)
                    {
                        Current = start;
                    }
                    else
                    {
                        Current++;
                    }
                    counter = 0;
                }
                return true;
            }
            return true;
        }

        public int GetCurrent()
        {
            return Current;
        }
    }

    public class Sprite
    {
        const int texelQuantityInTexture = 4;   //Number of rows and columns in one texture (eg 4x4 = 16)

        private SpriteState state;
        private Vector2 position;
        private float radius;
        private float angle;
        private Texture2D texture;
        private Texel[] texel;
        private int activeTexel;       

        private int texelSize;
        private Vector2 origin;
        private Point texelPos;
        private Rectangle sourceRectangle;
        private Rectangle rectangle;

        private float fadeTime;
        private float fadeTimeMax;

        #region Constructor

        public Sprite(Texture2D Texture, Vector2 Position, float Radius, int NumberOfTexels)
        {
            this.state = SpriteState.normal;
            this.texture = Texture;
            this.texelSize = Texture.Width / texelQuantityInTexture;
            this.origin = new Vector2(texelSize / 2, texelSize / 2);    //centre of drawn texel
            this.position = Position;
            this.radius = Radius;
            texel = new Texel[NumberOfTexels];
            this.angle = 0;
        }

        #endregion
        #region Loading/Saving

        public void Save(XmlWriter writer)
        {
            writer.WriteStartElement("Sprite");

            DataStore.WriteInt(writer, "SpriteState", (int)state);
            DataStore.WriteInt(writer, "ActiveTexel", activeTexel);
            DataStore.WriteFloat(writer, "FadeTime", fadeTime);
            DataStore.WriteFloat(writer, "FadeTimeMax", fadeTimeMax);

            writer.WriteEndElement();
        }

        public void Load(XmlReader reader)
        {
            do
            {
                // Only detect start elements.
                if (reader.IsStartElement())
                {
                    // Get element name and switch on it.
                    switch (reader.Name)
                    {
                        case "SpriteState":
                            state = (SpriteState)reader.ReadElementContentAsInt();
                            break;
                        case "ActiveTexel":
                            activeTexel = reader.ReadElementContentAsInt();
                            break;
                        case "FadeTime":
                            fadeTime = reader.ReadElementContentAsInt();
                            break;
                        case "FadeTimeMax":
                            fadeTimeMax = reader.ReadElementContentAsFloat();
                            break;
                        default:
                            reader.Read();
                            break;
                    }
                }
                else
                {
                    if (reader.NodeType == XmlNodeType.EndElement && (reader.Name == "Sprite")) break;
                    reader.Read();
                }               
            }
            while (!reader.EOF);
        }

        #endregion
        #region Public methods

        public bool Update(int texelID, float angle, float radius)
        {
            this.activeTexel = texelID;
            this.angle = angle;
            this.radius = radius;
            return this.Update();
        }

        public bool Update(int texelID, float angle)
        {
            this.activeTexel = texelID;
            this.angle = angle;
            return this.Update();
        }

        public bool Update(float angle)
        {
            this.angle = angle;
            return this.Update();
        }

        public bool Update(int texelID)
        {
            this.activeTexel = texelID;
            return this.Update();
        }

        public bool Update()
        {
            try
            {
                texel[activeTexel].Update();
                return true;
            }
            catch
            {
                throw new Exception("Not exesting texelID");
            }  
        }

        public bool Draw(SpriteBatch spriteBatch)
        {
            return this.Draw(spriteBatch, this.position);
        }

        public bool Draw(SpriteBatch spriteBatch, int texelID)
        {
            activeTexel = texelID;
            return Draw(spriteBatch, this.position);
        }

        public bool Draw(SpriteBatch spriteBatch, Vector2 drawPosition)
        {
            texelPos = new Point(texel[activeTexel].Current % texelQuantityInTexture, texel[activeTexel].Current / texelQuantityInTexture);

            sourceRectangle = new Rectangle(texelPos.X * texelSize, texelPos.Y * texelSize, texelSize, texelSize);
            rectangle = new Rectangle((int)(drawPosition.X), (int)(drawPosition.Y), (int)radius * 2, (int)radius * 2);

            switch (state)
            {
                case SpriteState.normal:
                    spriteBatch.Draw(texture, rectangle, sourceRectangle, Color.White, angle + 1.57f, origin, SpriteEffects.None, texel[activeTexel].layerDepth);
                    return true;
                case SpriteState.fadingOut:
                    fadeTime--;
                    if (fadeTime >= 0)
                    {
                        int color = (int)(255 * (fadeTime / fadeTimeMax));
                        spriteBatch.Draw(texture, rectangle, sourceRectangle, new Color(color, color, color, color), angle + 1.57f, origin, SpriteEffects.None, texel[activeTexel].layerDepth);
                        return true;
                    }
                    else
                    {
                        return false;
                    }
                default:
                    return false;
            }          
        }

        public bool Draw(SpriteBatch spriteBatch, Vector2 position, int texelID)
        {
            activeTexel = texelID;
            return this.Draw(spriteBatch, position);
        }

        public void AddTexel(int texelID, int Start, int End, int AnimInterval, int layerDepth)
        {
            texel[texelID] = new Texel(Start, End, AnimInterval, 0);
        }

        public void AddTexel(int texelID, int Start, int layerDepth)
        {
            texel[texelID] = new Texel(Start, layerDepth);
        }

        public void FadeOut(float fadeTime)
        {
            this.state = SpriteState.fadingOut;
            this.fadeTimeMax = fadeTime;
            this.fadeTime = fadeTime;
        }

        public void SetTexture(Texture2D texture)
        {
            this.texture = texture;
            this.texelSize = texture.Width / texelQuantityInTexture;
            this.origin = new Vector2(texelSize / 2, texelSize / 2);    //centre of drawn texel
        }

        #endregion
    }
}
